/**
 * Copyright 2012 Alfredo "Rainbowbreeze" Morresi
 * 
 * This file is part of Eureka! project.
 * 
 * Eureka! is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * Eureka! is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with Eureka!. If not, see http://www.gnu.org/licenses/.
 */
package it.rainbowbreeze.eureka.ui;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import it.rainbowbreeze.eureka.R;
import it.rainbowbreeze.eureka.arduino.BluetoothManager;
import it.rainbowbreeze.eureka.common.AppEnv;
import it.rainbowbreeze.eureka.common.LogFacility;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

/**
 * @author Alfredo "Rainbowbreeze" Morresi
 *
 */
public class ImportFragment extends Fragment {
    // ------------------------------------------ Private Fields
    private static final String LOG_HASH = ImportFragment.class.getSimpleName();
    
    private LogFacility mLogFacility;
    private Button mBtnEnableBluetooth;
    private Button mBtnDiscoverDevices;
    private Button mBtnGetData;

    private Button mBtnCheckArduinoBoard;
    private TextView mTxtSelectedDevice;
    private TextView mTxtIsArduino;

    private String mDeviceAddress;

    private Button mBtnSendOnCommand;

    private Button mBtnSendOffCommand;

    private BluetoothManager mBluetoothManager;



    // -------------------------------------------- Constructors

    // --------------------------------------- Public Properties

    // -------------------------------------------------- Events
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        
        AppEnv appEnv = AppEnv.i(getActivity().getApplicationContext());
        mLogFacility = appEnv.getLogFacility();
        
        mBluetoothManager = new BluetoothManager(mLogFacility);
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mLogFacility.v(LOG_HASH, "onCreateView");
        View view = inflater.inflate(R.layout.fraimport, container, false);
        
        mBtnEnableBluetooth = (Button) view.findViewById(R.id.import_btnToggleBluetooth);
        mBtnDiscoverDevices = (Button) view.findViewById(R.id.import_btnDetectDevices);
        mBtnGetData = (Button) view.findViewById(R.id.import_btnGetData);
        mBtnCheckArduinoBoard = (Button) view.findViewById(R.id.airpollution_btnCheckArduinoBoard);
        mBtnSendOnCommand = (Button) view.findViewById(R.id.airpollution_btnSendOnCommand);
        mBtnSendOffCommand = (Button) view.findViewById(R.id.airpollution_btnSendOffCommand);
        mTxtSelectedDevice = (TextView) view.findViewById(R.id.airpollution_txtSelectedDevice);
        mTxtIsArduino = (TextView) view.findViewById(R.id.airpollution_txtIsArduino);
        
        mBtnEnableBluetooth.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                mBluetoothManager.enableBluetooth(getActivity());
            }
        });
        
        mBtnDiscoverDevices.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                mBluetoothManager.startBluetoothDevicePicking(
                        false,
                        getActivity(),
                        new BluetoothManager.BluetoothDevicePickerListener() {
                            public void devicePicked(BluetoothDevice device) {
                                ImportFragment.this.devicePicked(getActivity(), device);
                            }
                        });
            }
        });
        
        mBtnCheckArduinoBoard.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                checkIfDeviceIsArduinoBoard(getActivity(), mDeviceAddress);
            }
        });
        
        mBtnSendOnCommand.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                sendData(mDeviceAddress, "a");
            }
        });
        
        mBtnSendOffCommand.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                sendData(mDeviceAddress, "o");
            }
        });
        
        //time to register broadcast receivers
        mBluetoothManager.onCreateTasks(
                getActivity(),
                new BluetoothManager.BluetoothStatusListener() {
                    public void bluetoothEnabled() {
                        ImportFragment.this.bluetoothEnabled();
                    }
                    public void bluetoothDisabled() {
                        ImportFragment.this.bluetoothDisabled();
                    }
                });
        
        //set enable/disable visibility
        setControlsStates(mBluetoothManager.isBluetoothEnabled());

        return view;
    }
    
    @Override
    public void onResume() {
        super.onResume();
        mBluetoothManager.onResumeTasks(getActivity());
    }
    
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mBluetoothManager.unregisterListeners(getActivity());
    }
    
    // ------------------------------------------ Public Methods

    // ----------------------------------------- Private Methods
    private void sendData(String deviceAddress, String data) {
        //TODO
        // The documents tell us to cancel the discovery process.
        // bluetoothAdapter.cancelDiscovery();
        BluetoothSocket socket = mBluetoothManager.createsSerialSocket(deviceAddress);
        if (null == socket) {
            mLogFacility.v(LOG_HASH, "Cannot connect to device");
            return;
        }
        mLogFacility.v(LOG_HASH, "Now sending " + data + " to device " + deviceAddress);
        OutputStream outputStream;
        try {
            outputStream = socket.getOutputStream();
            outputStream.write(data.getBytes());
        } catch (IOException e) {
            mLogFacility.e(LOG_HASH, "Error sending data", e);
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                mLogFacility.e(LOG_HASH, "Error closing socket", e);
            }
        }
    }
    
    private String readData(String deviceAddress) {
        //TODO
        // The documents tell us to cancel the discovery process.
        // bluetoothAdapter.cancelDiscovery();
        BluetoothSocket socket = mBluetoothManager.createsSerialSocket(deviceAddress);
        if (null == socket) {
            mLogFacility.v(LOG_HASH, "Cannot connect to device");
            return null;
        }
        mLogFacility.v(LOG_HASH, "Now reading data from device " + deviceAddress);
        try {
            int read = 0;
            byte[] buffer = new byte[50];
            InputStream in = socket.getInputStream();
            while (true) {
                read = in.read(buffer);
                StringBuilder buf = new StringBuilder();
                for (int i = 0; i < read; i++) {
                    int b = buffer[i] & 0xff;
                    if (b < 0x10) {
                        buf.append("0");
                    }
                    buf.append(Integer.toHexString(b)).append(" ");
                }
                mLogFacility.v(LOG_HASH, "Read "+ read +" bytes: "+ buf.toString());
            }
        } catch (IOException e) {
            mLogFacility.e(LOG_HASH, "Error reading data" + deviceAddress);
        }
        
        return null;
    }
    
    private void setControlsStates(boolean bluetoothState) {
        mBtnEnableBluetooth.setEnabled(!bluetoothState);
        mBtnDiscoverDevices.setEnabled(bluetoothState);
        mBtnGetData.setEnabled(false);
        mBtnCheckArduinoBoard.setEnabled(bluetoothState);
        mTxtSelectedDevice.setEnabled(bluetoothState);
        mBtnSendOffCommand.setEnabled(bluetoothState);
        mBtnSendOnCommand.setEnabled(bluetoothState);
        mTxtIsArduino.setEnabled(bluetoothState);
        mTxtIsArduino.setText(bluetoothState ? "---" : "");
    }

    private void bluetoothEnabled() {
        setControlsStates(true);
    }

    private void bluetoothDisabled() {
        setControlsStates(false);
    }

    /**
     * Executed when the user choose a new bluetooth device to connect to
     * @param device
     * @return
     */
    private void devicePicked(Activity activity, BluetoothDevice device) {
        if (null == device) {
            mLogFacility.i(LOG_HASH, "Null bluetooth device selected");
            return;
        }
        mDeviceAddress = device.getAddress();
        mLogFacility.v(LOG_HASH, "New bluetooth device selected: " + mDeviceAddress);
        mTxtSelectedDevice.setText(mDeviceAddress);
        
        //verifies bond state
        mBluetoothManager.bondSecureDevice(activity, mDeviceAddress, "1234");
    }

    
    
    /**
     * Discoveries services of bluetooth device and check if it's an Arduino board
     * 
     * @param activity
     * @param deviceAddress
     */
    private void checkIfDeviceIsArduinoBoard(Activity activity, final String deviceAddress) {
        if (TextUtils.isEmpty(deviceAddress)) {
            Toast.makeText(getActivity(), "No device selected", Toast.LENGTH_SHORT).show();
            return;
        }
        
        BluetoothDevice device = mBluetoothManager.getRemoteDevice(mDeviceAddress);
        
        BluetoothManager.ServiceDiscoveryListener listener = new BluetoothManager.ServiceDiscoveryListener() {
            private boolean isArduino = false;
            
            public void serviceDiscoveryStarted() {
                mTxtIsArduino.setText("Started check...");
            }
            
            public void serviceDiscoveryFinished(UUID[] uuids) {
                if (null == uuids) {
                    mLogFacility.v(LOG_HASH, "No UUID found for the remote device " + deviceAddress);
                    mTxtIsArduino.setText("Unable to analyze device");
                } else {
                    for(UUID uuid : uuids) {
                        mLogFacility.v(LOG_HASH, "Find UUID " + uuid.toString());
                        if (BluetoothManager.ServiceClasses.SerialPortServiceClass.equalsIgnoreCase(uuid.toString())) {
                            isArduino = true;
                        }
                    }
                    mTxtIsArduino.setText(isArduino ? "Arduino found!" : "Not an Arduino device");
                }
            }
            
            public void serviceDiscoveryFailed() {
                mTxtIsArduino.setText("Unable to discover device's services");
            }
        };
        
        mBluetoothManager.discoverDeviceServices(getActivity().getApplicationContext(), device, listener);
        
    }


    
    // ----------------------------------------- Private Classes
}
